//
// (C) Copyright 2009 Irantha Suwandarathna (irantha@gmail.com)
// All rights reserved.
//

/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using EffiProz.Core.Lib;
using EffiProz.Core.Errors;


namespace EffiProz.Core
{
    // fredt@users 20020420 - patch523880 by leptipre@users - VIEW support - modified
    // fredt@users 20031227 - remimplementated as compiled query

    /**
     * Represents an SQL VIEW based on a SELECT statement.
     *
     * @author leptipre@users
     * @author fredt@users
     * @version 1.8.0
     * @since 1.7.0
     */
    public class View : TableDerived
    {
        public SubQuery viewSubQuery;
        public String statement;
        //
        public QNameManager.QName[] columnNames;

        /** schema at the time of compilation */
        public QNameManager.QName compileTimeSchema;

        /**
         * List of subqueries in this view in order of materialization. Last
         * element is the view itself.
         */
        public SubQuery[] viewSubqueries;

        /**
         * Names of SCHEMA objects referenced in VIEW
         */
        OrderedHashSet schemaObjectNames;

        /**
         * check option
         */
        int check;

        //
        private Table baseTable;

        //
        Expression checkExpression;

        public View(Session session, Database db, QNameManager.QName name, QNameManager.QName[] columnNames,
                String definition, int check)
            : base(db, name, TableBase.VIEW_TABLE)
        {
            this.columnNames = columnNames;
            this.statement = definition;
            this.check = check;
            compileTimeSchema = session.getSchemaQName(null);
        }

        public override int getType()
        {
            return SchemaObjectTypes.VIEW;
        }

        public override OrderedHashSet getReferences()
        {
            return schemaObjectNames;
        }

        public override OrderedHashSet getComponents()
        {
            return null;
        }

        /**
         * Compiles the query expression and sets up the columns.
         */
        public override void compile(Session session, SchemaObject parentObject)
        {

            if (!database.schemaManager.schemaExists(compileTimeSchema.name))
            {
                compileTimeSchema = session.getSchemaQName(null);
            }

            session.setSchema(compileTimeSchema.name);

            ParserDQL p = new ParserDQL(session, new Scanner(statement));

            p.read();

            viewSubQuery = p.XreadViewSubquery(this);
            queryExpression = viewSubQuery.queryExpression;

            if (getColumnCount() == 0)
            {
                if (columnNames == null)
                {
                    columnNames =
                        viewSubQuery.queryExpression.getResultColumnNames();
                }

                if (columnNames.Length
                        != viewSubQuery.queryExpression.getColumnCount())
                {
                    throw Error.error(ErrorCode.X_42593, tableName.statementName);
                }

                TableUtil.setColumnsInSchemaTable(
                    this, columnNames, queryExpression.getColumnTypes(),
                    queryExpression.getColumnNullability());
            }

            //
            viewSubqueries = p.compileContext.getSubqueries();

            for (int i = 0; i < viewSubqueries.Length; i++)
            {
                if (viewSubqueries[i].parentView == null)
                {
                    viewSubqueries[i].parentView = this;
                }
            }

            //
            viewSubQuery.getTable().view = this;
            viewSubQuery.getTable().columnList = columnList;
            schemaObjectNames = p.compileContext.getSchemaObjectNames();
            baseTable = queryExpression.getBaseTable();

            if (baseTable == null)
            {
                return;
            }

            switch (check)
            {

                case SchemaObjectViewCheckModes.CHECK_NONE:
                    break;

                case SchemaObjectViewCheckModes.CHECK_LOCAL:
                    checkExpression = queryExpression.getCheckCondition();
                    break;

                case SchemaObjectViewCheckModes.CHECK_CASCADE:
                    break;

                default:
                    throw Error.runtimeError(ErrorCode.U_S0500, "View");
            }
        }

        public override String getSQL()
        {

            StringBuilder sb = new StringBuilder(128);

            sb.Append(Tokens.T_CREATE).Append(' ').Append(Tokens.T_VIEW);
            sb.Append(' ');
            sb.Append(getName().getSchemaQualifiedStatementName()).Append(' ');
            sb.Append('(');

            int count = getColumnCount();

            for (int j = 0; j < count; j++)
            {
                sb.Append(getColumn(j).getName().statementName);

                if (j < count - 1)
                {
                    sb.Append(',');
                }
            }

            sb.Append(')').Append(' ').Append(Tokens.T_AS).Append(' ');
            sb.Append(getStatement());

            return sb.ToString();
        }

        public override int[] getUpdatableColumns()
        {
            return queryExpression.getBaseTableColumnMap();
        }

        public int getCheckOption()
        {
            return check;
        }

        /**
         * Returns the query expression for the view.
         */
        public String getStatement()
        {
            return statement;
        }

        /**
         * Overridden to disable SET TABLE READONLY DDL for View objects.
         */
        public override void setDataReadOnly(bool value)
        {
            throw Error.error(ErrorCode.X_28000);
        }

        public void collectAllFunctionExpressions(OrderedHashSet collector)
        {

            // filter schemaObjectNames
        }

    }

}